﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

//malloc
//动态内存管理作用：灵活申请和释放空间
//动态内存开辟函数,包含头文件stdlib.h
//功能：向内存的堆区申请⼀块连续可⽤的空间,单位字节并返回指向这块空间的起始地址。
//void* malloc(size_t size)
//如果开辟成功，则返回这块空间的起始地址。
//1.如果开辟失败,则返回⼀个NULL指针,因此malloc的返回值⼀定要做检查。
//2.返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃⼰来决定。
//3.如果参数size为0,malloc的⾏为是标准是未定义的,取决于编译器

//int main() {
//	int arr[10] = { 0 };//栈区
//	int *p = (int *)malloc(40);
//	if (p == NULL) {
//		printf("%s\n", strerror(errno));
//		return 1;
//	}
//	int i = 0;
//	for (i = 0; i < 10; i++) {
//		*(p + i) = i;
//		printf("%d ", *(p + i));
//	}
//	//没有free,并不是内存空间就不回收了
//	//当程序退出时，系统会自动回收内存空间的
//	//内存泄露：申请了空间，不还给系统
//	free(p);
//	p = NULL;
//	//先释放再赋空指针，避免p还能找到已被释放空间的地址
//	return 0;
//}

//free函数：必须释放动态开辟的内存
//void free (void* ptr)
//头文件 stdlib.h
//1.如果参数ptr指向的空间不是动态开辟的,free函数的⾏为是未定义的
//2.如果参数ptr是NULL指针,则函数什么事都不做

//calloc函数：⽤来动态内存分配
//void* calloc(size_t num,size_t size)
//功能:num个⼤⼩为size的元素开辟⼀块空间,并且把空间的每个字节初始化为0
//int main()
//{
//	int* p = (int*)calloc(10, sizeof(int));
//	if (NULL != p)
//	{
//		int i = 0;
//		for (i = 0; i < 10; i++)
//		{
//			printf("%d ", *(p + i));
//		}
//	}
//	free(p);
//	p = NULL;
//	return 0;
//}

//realloc:动态内存管理更加灵活,调整动态开辟内存的⼤⼩
//void* realloc(void* ptr,size_t size)
//ptr 是要调整的内存地址
//size是调整之后的新⼤⼩,单位是字节
//返回值为调整之后的内存起始位置。
//这个函数调整原内存空间⼤⼩的基础上，还会将原来内存中的数据移动到 新 的空间

//情况1：原有空间之后有⾜够⼤的空间
//扩展内存就直接原有内存之后直接追加空间，原来空间的数据不发⽣变化
//情况2：原有空间之后没有⾜够⼤的空间
//原有空间之后没有⾜够多的空间时,在堆空间上另找⼀个合适⼤⼩的连续空间来使⽤
//这样函数返回的是⼀个新的内存地址,旧空间会被自动释放掉
//
//int main() {
//		int *p = (int *)malloc(40);
//		if (p == NULL) {
//			printf("%s\n", strerror(errno));
//			return 1;
//		}
//		int i = 0;
//		for (i = 0; i < 10; i++) {
//			*(p + i) = i;
//			printf("%d ", *(p + i));
//		}
//	//扩容
//		//要先判断扩容成功不为空指针在赋给p
//		int* ptr = realloc(p, 80);
//		if (ptr != NULL) {
//			p = ptr;
//		}
//		for (i = 0; i < 10; i++) {
//			printf("%d ", *(p + i));
//		}
//		free(p);
//		p = NULL;
//		//realloc(NULL,40)等价于malloc(30);
//		return 0;
//	}

//常见动态内存错误
//1.对NULL指针的解引用操作：一定要判断动态空间是否开辟成功
//2.越界访问
//int main() {
//	int* p = (int*)malloc(40);
//	if (p == NULL) {
//		printf("%s\n", strerror(errno));
//		return 1;
//	}
//	int i = 0;
//	//11个元素，越界访问了
//	for (i = 0; i <= 10; i++) {
//		*(p + i) = i;
//	}
//	free(p);
//	p = NULL;
//	return 0;
//}
//3.对非动态开辟内存使用free释放
//4.使用free释放一块动态开辟内存的一部分
//int main() {
//	int* p = (int*)malloc(40);
//	if (p == NULL) {
//		printf("%s\n", strerror(errno));
//		return 1;
//	}
//	int i = 0;
//	for (i = 0; i < 10; i++) {
//		*p = i;
//		p++;
//		//改变了p的地址，指向了这块空间的最后的位置，而不是起始位置
//	}
//	free(p);
//	p = NULL;
//	return 0;
//}
//5.对同一块动态内存的多次释放
//free完指针后要进行置空
//6.动态内存开辟忘记释放（内存泄露）

//经典笔试
//一
//void GetMemory(char* p)
//{
//	p = (char*)malloc(100);
//	//p指向开辟的100个字节地址起始处
//	//由于出函数p指针就会被系统操作回收
//	//会使100字节的内存空间泄露，开辟了无法使用也未释放
//}
//void Test(void)
//{
//	char* str = NULL;
//	GetMemory(str);
//	//p指针形参，影响不了实参str
//	//str仍是NULL，解引用会崩溃
//	strcpy(str, "hello world");
//	printf(str);
//}
//int main()
//{
//	Test();
//	return 0;
//}
//二
//char* GetMemory(void)
//{
//	char p[] = "hello world";
//	return p;
//	//虽然返回了p的地址
//	//但系统回收了p的空间，这块空间会分配给其他
//	//可能会被其他内容覆盖改变
//}
//void Test(void)
//{
//	char* str = NULL;
//	str = GetMemory();
//	//str确实指向p还未被回收时的地址
//	//但当p被回收后这个地址的内容就会可能改变
//	//str指针指向的地址空间已经不属于该程序
//	printf(str);
//}
//int main()
//{
//	Test();
//	return 0;
//}
//三
//void GetMemory(char** p, int num)
//{
//	*p = (char*)malloc(num);
//}
//void Test(void)
//{
//	char* str = NULL;
//	GetMemory(&str, 100);
//	strcpy(str, "hello");
//	printf(str);
//	//忘记释放了
//}
//
//int main()
//{
//	Test();
//	return 0;
//}
//四
//void Test(void)
//{
//	char* str = (char*)malloc(100);
//	strcpy(str, "hello");
//	free(str);
//	//空间释放，不属于该程序
//	if (str != NULL)
//	{
//		strcpy(str, "world");
//		//str已经是野指针，形成非法访问
//		printf(str);
//	}
//}
//int main()
//{
//	Test();
//	return 0;
//}